import datetime
import json
import os
import re
import shutil
import time
import urllib.parse
import zipfile
from pathlib import Path

from django.http import HttpResponse, JsonResponse, FileResponse
from django.shortcuts import render
os.environ["TZ"] = "Asia/Shanghai"
time.tzset()
from loguru import logger

# Create your views here.
server_file = "./server_file"


def sanitize_filename(filename):
    # 定义不允许的字符正则表达式
    disallowed_chars = r'[\/:*?"<>|()（）]'  # 这里列出了一些常见的不允许字符
    # 使用正则表达式查找不合理的字符并替换为空格
    sanitized_filename = re.sub(disallowed_chars, '_', filename)
    return sanitized_filename
def test(request):
    return HttpResponse("欢迎使用common_app")

def get_json_file(request,self_app):
    '''
    URL地址：/self_app/getJsonFile
    接口方法：GET/POST
    请求参数：
        JsonFileName：
        我会把每个APP的所有JSON的文件放到 /server_file/SelfApp/ 文件夹下
        如果JsonFileName= test.json,self_app=common
        那么我会返回/server_file/common/jsonFile/test.json
        要保证JsonFileName必须是json文件
    '''
    logger.info("start")
    JsonFileName = ""
    SelfApp = self_app
    if request.method == "GET":
        JsonFileName = request.GET.get('JsonFileName')

    elif request.method == "POST":
        JsonFileName = request.POST.get('JsonFileName')
    else:
        return HttpResponse("请用POST或者GET方法获得JsonFile")

    if not JsonFileName:
        return HttpResponse("缺少请求参数：JsonFileName")

    jsonPath = os.path.join(server_file, SelfApp,"jsonFile",JsonFileName)
    logger.info("请求JsonFile：{}".format(jsonPath))

    if not os.path.exists(jsonPath):
        msg = '{} 不存在'.format(jsonPath)
        logger.error(msg)
        return HttpResponse(msg)

    with open(jsonPath) as f:
        jsonData = json.load(f)
    logger.info("end")
    return JsonResponse(jsonData)

def get_resource(request,self_app):
    '''
    URL地址：/self_app/getResource/
    接口方法：GET/POST
    请求参数：
        ResourceName：必需
        ResourcePath：默认为 .

        我会把每个APP的所有资源文件放到 /server_file/self_app/resource/ResourcePath 文件夹下
        如果ResourceName= test.png,self_app=common,ResourcePath="picture"
        那么我会返回/server_file/common/resource/picture/test.png 文件
    '''
    logger.info("start")
    ResourceName = ""
    ResourcePath = ""
    SelfApp = self_app
    if request.method == "GET":
        ResourceName = request.GET.get('ResourceName')
        ResourcePath = request.GET.get('ResourcePath',"")

    elif request.method == "POST":
        ResourceName = request.POST.get('ResourceName')
        ResourcePath = request.POST.get('ResourcePath',"")
    else:
        return HttpResponse("请用POST或者GET方法获得Resource")


    if not ResourceName:
        return HttpResponse("缺少请求参数：ResourceName")


    resourcePath = os.path.join(server_file, SelfApp, "resource", ResourcePath,ResourceName)
    logger.info("请求resource：{}".format(resourcePath))
    if not Path(resourcePath).is_file():
        msg = '{} 不存在'.format(resourcePath)
        logger.error(msg)
        return HttpResponse(msg)

    with open(resourcePath, 'rb') as file:
        response = HttpResponse(file.read())
    response["Access-Control-Expose-Headers"] = "content-disposition" #向前端暴露content-disposition字段
    chinese_pattern = re.compile(r'[\u4e00-\u9fa5]')
    oriTail = chinese_pattern.sub('', ResourceName)
    encoded_filename = urllib.parse.quote(ResourceName, safe="@:~!#$%^&*")
    response['Content-Disposition'] = f'attachment; filename={oriTail}; filename*=utf-8\'\'{encoded_filename}'

    logger.info("end")
    return response

def download_file(request,self_app):
    '''
    URL地址：/self_app/download
    接口方法：GET/HEAD
    请求参数：
        filePath：默认为 .
        fileName：默认为 ""
    我会把通过此接口下载的文件放到./tmp/self_app/filePath/fileName
    注意：filePath和fileName至少要下发一个
    '''
    def parse_range_header(range_header, file_size):
        start, end = range_header.replace('bytes=', '').split('-')
        start = int(start)
        end = int(end) if end else file_size - 1
        return start, end
    def CheckPathLega(path):
        if path.endswith('/') or path.endswith('.'):
            path = path[:-1]  # 使用切片操作去掉最后一个字符
        return path
    def zip_folder(folder_path, output_zip_path):
        try:
            with zipfile.ZipFile(output_zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
                for root, _, files in os.walk(folder_path):
                    for file in files:
                        file_path = os.path.join(root, file)
                        arcname = os.path.relpath(file_path, folder_path)
                        zipf.write(file_path, arcname)
        except Exception as e:
            print(f"压缩文件夹失败: {e}")
    def delete_folder(self,path):
        """
        递归删除文件夹
        """
        if os.path.isdir(path):
            for sub_path in os.listdir(path):
                sub_path = os.path.join(path, sub_path)
                self.delete_folder(sub_path)
            shutil.rmtree(path)
        else:
            os.remove(path)
    def get_send_file_path(request,is_dir_zip=False):
        filePath = request.GET.get('filePath', '.')
        fileName = request.GET.get('fileName', "")

        if (filePath == '.' or filePath == '')and fileName == '':
            msg = "filePath and fileName must have at least one"
            logger.error(msg)
            response = HttpResponse(status=304)
            response['data'] = msg
            return response

        server_file_path = os.path.join('./tmp/', self_app, filePath, fileName)
        #server_file_path的拼接决定了后端可以向前端开放那些路径
        if is_dir_zip:
            logger.info("\nfilePath：{}\nfileName：{}".format(filePath, fileName))
            logger.info("\nserver_file_path：{}".format(server_file_path))

        if Path(server_file_path).is_dir():
            last_dir = os.path.basename(os.path.normpath(server_file_path))
            parent_dir = os.path.dirname(os.path.normpath(server_file_path))
            sendFilePath = os.path.join(parent_dir, last_dir + ".zip")

            if is_dir_zip:
                logger.info("i will send {}".format(sendFilePath))
                if Path(sendFilePath).is_file():
                    # 如果已经存在同名的压缩包，则把之前的压缩包进行重命名
                    currentDatetime = datetime.datetime.now().strftime("%Y-%m-%d--%H:%M:%S")
                    dir, name = os.path.split(sendFilePath)
                    newName = os.path.join(dir, name + "---" + currentDatetime)
                    os.rename(sendFilePath, newName)
                    logger.info("\n{}\nrename\n{}".format(sendFilePath, newName))

                zip_folder(server_file_path, sendFilePath)  # 压缩完毕
        elif Path(server_file_path).is_file():
            # 如果是文件，就直接发送就好了
            sendFilePath = server_file_path
            logger.info("i will send {}".format(sendFilePath))
        else:
            msg = "{} does not exist".format(server_file_path)
            sendFilePath = msg
            logger.error(msg)
        return sendFilePath
    if request.method == 'HEAD':
        #只拿文件或者文件夹（会压缩）的信息
        sendFilePath = str(get_send_file_path(request,is_dir_zip=True))
        if "does not exist" in sendFilePath:
            response = HttpResponse(status=304)
            response['data'] = sendFilePath
            return response
        # 获取文件大小
        file_size = os.path.getsize(sendFilePath)
        # logger.info(file_size)
        # 设置响应头，返回文件大小信息
        response = HttpResponse(status=206)
        response['File-Size'] = file_size
        response['Accept-Ranges'] = 'bytes'  # 告知客户端支持分片下载
        head, tail = os.path.split(sendFilePath)
        # 设置文件名并进行 URL 编码
        chinese_pattern = re.compile(r'[\u4e00-\u9fa5]')
        oriTail = chinese_pattern.sub('', tail)
        encoded_filename = urllib.parse.quote(tail, safe="@:~!#$%^&*")
        response['Content-Disposition'] = f'attachment; filename={oriTail}; filename*=utf-8\'\'{encoded_filename}'
        # 可根据需要设置其他相关响应头，例如 Content-Type
        response['Content-Type'] = 'application/octet-stream'

        response["Access-Control-Expose-Headers"] = "content-disposition,file-size,accept-ranges"  # 向前端暴露content-disposition字段
        return response

    elif request.method == 'GET':
        #开始真正的下载文件
        sendFilePath = str(get_send_file_path(request))
        if "does not exist" in sendFilePath:
            response = HttpResponse(status=304)
            response['data'] = sendFilePath
            return response
        if Path(sendFilePath).is_file():
            range_header = request.headers.get('Range', None)
            if not range_header:
                logger.error("no range")
                logger.error(range_header)
                logger.error(request.headers)
                return "Range header is required.", 400
            file_size = os.path.getsize(sendFilePath)
            start, end = parse_range_header(range_header, file_size)
            with open(sendFilePath, 'rb') as file:
                file.seek(start)
                data = file.read(end - start + 1)
            response = HttpResponse(data,status=206)
            response['Content-Range'] = f'bytes {start}-{end}/{file_size}'
            response['Content-Length'] = len(data)
            return response


def upload_file(request,self_app):
    '''
    URL地址：/self_app/upload
    接口方法:POST
    请求参数：
        filePath:
        存取的路径，非必需，默认为 .
        fileName：
        重新命名的名字，非必需，默认使用原来的名字
        checkDuplicates:
        是否检查重复，默认为No
        如果为Dir path，服务器会检查有没有同名的文件夹，如果有重命名一个新的，并且把新路径返回
        如果为File，服务器会检查有没有同名的文件，如果有重命名一个新的，并且把新路径返回
    返回值：
        返回储存成功的文件路径和名字
    前端上传的文件我会存在./tmp/self_app/filePath/fileName
    注意：前端表单的key，单文件请使用file，多文件请使用files[]
    '''
    server_files = []
    if request.method == 'POST':
        try:
            file = request.FILES['file']
        except:
            logger.info("request.FILES['file'] is null")
            file = ''
        files = request.FILES.getlist('files[]')

        file_path = request.POST.get('filePath', '.')
        file_path = file_path[1:] if file_path.startswith('/') else file_path
        logger.info("\nfile_path：{}".format(file_path))
        check_duplicates = request.POST.get('checkDuplicates','No')
        if check_duplicates.startswith("DIR"):
            check_duplicates_path = check_duplicates.split(" ")[1]
            if os.path.exists(os.path.join('./tmp/',self_app,check_duplicates_path)):
                currentDatetime = datetime.datetime.now().strftime("%Y-%m-%d--%H:%M:%S")
                result = os.path.commonpath([os.path.normpath(check_duplicates_path),os.path.normpath(file_path)])
                result = os.path.dirname(result)
                xu = os.path.relpath(file_path, result)
                file_path = os.path.join(result, currentDatetime, xu)
                # file_path = os.path.normpath(file_path)
                logger.info("\nrepair file_path：{}".format(file_path))

        elif check_duplicates.startswith("FILE"):
            pass
        else:
            pass
        if file:
            file_name = request.POST.get('fileName', file.name)
            # server_file_ori_path = os.path.join('./tmp/', self_app)
            server_file_save_path = os.path.join('./tmp/',self_app,file_path)
            server_file = os.path.join(server_file_save_path, file_name)

            server_files.append(os.path.normpath(os.path.join('./', file_path, file_name)))
            logger.info("server_file_save_path:{}".format(server_file_save_path))

            if not os.path.exists(server_file_save_path):
                os.makedirs(server_file_save_path)
            else:
                logger.error("exists")

            with open(server_file, 'wb') as destination:
                for chunk in file.chunks():
                    destination.write(chunk)
        elif files:
            file_names = request.POST.get('fileNames[]', [_file.name for _file in files])
            if len(file_names) != len(files):
                file_names = [_file.name for _file in files]
            logger.info(file_names)
            for _file ,_file_name in zip(files,file_names):
                server_file_ori_path = os.path.join('./tmp/', self_app)
                server_file_save_path = os.path.join(server_file_ori_path, file_path)
                server_file = os.path.join(server_file_save_path, _file_name)
                server_files.append(os.path.join('./', file_path, _file_name))
                if not os.path.exists(server_file_save_path):
                    os.makedirs(server_file_save_path)
                with open(server_file, 'wb') as destination:
                    for chunk in _file.chunks():
                        destination.write(chunk)

    return JsonResponse(server_files,safe=False)
        # server_file_path的拼接决定了前端传来的文件存储在哪里

